/*
 * Copyright (c) XMEDIA. All rights reserved.
 */

#ifndef __XMEDIA_FMC100_H__
#define __XMEDIA_FMC100_H__

#include <linux/platform_device.h>
#include <linux/mfd/xmedia_fmc.h>

#define INFINITE            (0xFFFFFFFF)

#define SPI_IF_READ_STD         (0x01)
#define SPI_IF_READ_FAST        (0x02)
#define SPI_IF_READ_DUAL        (0x04)
#define SPI_IF_READ_DUAL_ADDR       (0x08)
#define SPI_IF_READ_QUAD        (0x10)
#define SPI_IF_READ_QUAD_ADDR       (0x20)

#define SPI_IF_WRITE_STD        (0x01)
#define SPI_IF_WRITE_DUAL       (0x02)
#define SPI_IF_WRITE_DUAL_ADDR      (0x04)
#define SPI_IF_WRITE_QUAD       (0x08)
#define SPI_IF_WRITE_QUAD_ADDR      (0x10)

#define SPI_IF_ERASE_SECTOR_4K      (0x01)
#define SPI_IF_ERASE_SECTOR_32K     (0x02)
#define SPI_IF_ERASE_SECTOR_64K     (0x04)
#define SPI_IF_ERASE_SECTOR_128K    (0x08)
#define SPI_IF_ERASE_SECTOR_256K    (0x10)

#define XMEDIA_FMC_SPI_NAND_SUPPORT_READ (SPI_IF_READ_STD \
					| SPI_IF_READ_FAST \
					| SPI_IF_READ_DUAL \
					| SPI_IF_READ_DUAL_ADDR \
					| SPI_IF_READ_QUAD \
					| SPI_IF_READ_QUAD_ADDR)

#define XMEDIA_FMC_SPI_NAND_SUPPORT_WRITE    (SPI_IF_WRITE_STD | SPI_IF_WRITE_QUAD)

#define XMEDIA_FMC_SPI_NAND_SUPPORT_MAX_DUMMY    8

#define SPI_CMD_READ_STD        0x03    /* Standard read cache */
#define SPI_CMD_READ_FAST       0x0B    /* Higher speed read cache */
#define SPI_CMD_READ_DUAL       0x3B    /* 2 IO read cache only date */
#define SPI_CMD_READ_DUAL_ADDR      0xBB    /* 2 IO read cache date&addr */
#define SPI_CMD_READ_QUAD       0x6B    /* 4 IO read cache only date */
#define SPI_CMD_READ_QUAD_ADDR      0xEB    /* 4 IO read cache date&addr */

#define SPI_CMD_WRITE_STD       0x02    /* Standard page program */
#define SPI_CMD_WRITE_DUAL      0xA2    /* 2 IO program only date */
#define SPI_CMD_WRITE_DUAL_ADDR     0xD2    /* 2 IO program date&addr */
#define SPI_CMD_WRITE_QUAD      0x32    /* 4 IO program only date */
#define SPI_CMD_WRITE_QUAD_ADDR     0x12    /* 4 IO program date&addr */

#define SPI_CMD_SE_4K           0x20    /* 4KB sector Erase */
#define SPI_CMD_SE_32K          0x52    /* 32KB sector Erase */
#define SPI_CMD_SE_64K          0xD8    /* 64KB sector Erase */
#define SPI_CMD_SE_128K         0xD8    /* 128KB sector Erase */
#define SPI_CMD_SE_256K         0xD8    /* 256KB sector Erase */

#define SET_READ_STD(_dummy_, _size_, _clk_) \
    static struct spi_op read_std_##_dummy_##_size_##_clk_ = { \
    SPI_IF_READ_STD, SPI_CMD_READ_STD, _dummy_, _size_, _clk_ }

#define SET_READ_FAST(_dummy_, _size_, _clk_) \
    static struct spi_op read_fast_##_dummy_##_size_##_clk_ = { \
    SPI_IF_READ_FAST, SPI_CMD_READ_FAST, _dummy_, _size_, _clk_ }

#define SET_READ_DUAL(_dummy_, _size_, _clk_) \
    static struct spi_op read_dual_##_dummy_##_size_##_clk_ = { \
    SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL, _dummy_, _size_, _clk_ }

#define SET_READ_DUAL_ADDR(_dummy_, _size_, _clk_) \
    static struct spi_op read_dual_addr_##_dummy_##_size_##_clk_ = { \
    SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR, _dummy_, _size_, _clk_ }

#define SET_READ_QUAD(_dummy_, _size_, _clk_) \
    static struct spi_op read_quad_##_dummy_##_size_##_clk_ = { \
    SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD, _dummy_, _size_, _clk_ }

#define SET_READ_QUAD_ADDR(_dummy_, _size_, _clk_) \
    static struct spi_op read_quad_addr_##_dummy_##_size_##_clk_ = { \
    SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR, _dummy_, _size_, _clk_ }

#define SET_WRITE_STD(_dummy_, _size_, _clk_) \
    static struct spi_op write_std_##_dummy_##_size_##_clk_ = { \
    SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD, _dummy_, _size_, _clk_ }

#define SET_WRITE_DUAL(_dummy_, _size_, _clk_) \
    static struct spi_op write_dual_##_dummy_##_size_##_clk_ = { \
    SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL, _dummy_, _size_, _clk_ }

#define SET_WRITE_DUAL_ADDR(_dummy_, _size_, _clk_) \
    static struct spi_op write_dual_addr_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR, _dummy_, _size_, _clk_ }

#define SET_WRITE_QUAD(_dummy_, _size_, _clk_) \
    static struct spi_op write_quad_##_dummy_##_size_##_clk_ = { \
    SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD, _dummy_, _size_, _clk_ }

#define SET_WRITE_QUAD_ADDR(_dummy_, _size_, _clk_) \
    static struct spi_op write_quad_addr_##_dummy_##_size_##_clk_ = { \
SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR, _dummy_, _size_, _clk_ }

#define SET_ERASE_SECTOR_4K(_dummy_, _size_, _clk_) \
    static struct spi_op erase_sector_4k_##_dummy_##_size_##_clk_ = { \
    SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K, _dummy_, _size_, _clk_ }

#define SET_ERASE_SECTOR_32K(_dummy_, _size_, _clk_) \
    static struct spi_op erase_sector_32k_##_dummy_##_size_##_clk_ = { \
    SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K, _dummy_, _size_, _clk_ }

#define SET_ERASE_SECTOR_64K(_dummy_, _size_, _clk_) \
    static struct spi_op erase_sector_64k_##_dummy_##_size_##_clk_ = { \
    SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K, _dummy_, _size_, _clk_ }

#define SET_ERASE_SECTOR_128K(_dummy_, _size_, _clk_) \
    static struct spi_op erase_sector_128k_##_dummy_##_size_##_clk_ = { \
    SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K, _dummy_, _size_, _clk_ }

#define SET_ERASE_SECTOR_256K(_dummy_, _size_, _clk_) \
    static struct spi_op erase_sector_256k_##_dummy_##_size_##_clk_ = { \
    SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K, _dummy_, _size_, _clk_ }

#define READ_STD(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_
#define READ_FAST(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_
#define READ_DUAL(_dummy_, _size_, _clk_) read_dual_##_dummy_##_size_##_clk_
#define READ_DUAL_ADDR(_dummy_, _size_, _clk_) \
	read_dual_addr_##_dummy_##_size_##_clk_
#define READ_QUAD(_dummy_, _size_, _clk_) read_quad_##_dummy_##_size_##_clk_
#define READ_QUAD_ADDR(_dummy_, _size_, _clk_) \
	read_quad_addr_##_dummy_##_size_##_clk_

#define WRITE_STD(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_
#define WRITE_DUAL(_dummy_, _size_, _clk_) write_dual_##_dummy_##_size_##_clk_
#define WRITE_DUAL_ADDR(_dummy_, _size_, _clk_) \
	write_dual_addr_##_dummy_##_size_##_clk_
#define WRITE_QUAD(_dummy_, _size_, _clk_) write_quad_##_dummy_##_size_##_clk_
#define WRITE_QUAD_ADDR(_dummy_, _size_, _clk_) \
	write_quad_addr_##_dummy_##_size_##_clk_

#define ERASE_SECTOR_4K(_dummy_, _size_, _clk_) \
	erase_sector_4k_##_dummy_##_size_##_clk_
#define ERASE_SECTOR_32K(_dummy_, _size_, _clk_) \
	erase_sector_32k_##_dummy_##_size_##_clk_
#define ERASE_SECTOR_64K(_dummy_, _size_, _clk_) \
	erase_sector_64k_##_dummy_##_size_##_clk_
#define ERASE_SECTOR_128K(_dummy_, _size_, _clk_) \
	erase_sector_128k_##_dummy_##_size_##_clk_
#define ERASE_SECTOR_256K(_dummy_, _size_, _clk_) \
	erase_sector_256k_##_dummy_##_size_##_clk_

#define SPI_CMD_WREN            0x06    /* Write Enable */
#define SPI_CMD_WRDI            0x04    /* Write Disable */

#define SPI_CMD_RDID            0x9F    /* Read Identification */

#define SPI_CMD_GET_FEATURES        0x0F    /* Get Features */
#define SPI_CMD_SET_FEATURE     0x1F    /* Set Feature */

#define SPI_CMD_PAGE_READ       0x13    /* Page Read to Cache */

#define SPI_CMD_RESET           0xff    /* Reset the device */

/* These macroes are for debug only, reg option is slower then dma option */
#undef XMEDIA_FMC100_SPI_NAND_SUPPORT_REG_READ
/* #define XMEDIA_FMC100_SPI_NAND_SUPPORT_REG_READ */

#undef XMEDIA_FMC100_SPI_NAND_SUPPORT_REG_WRITE
/* #define XMEDIA_FMC100_SPI_NAND_SUPPORT_REG_WRITE */

#ifdef CONFIG_XMEDIA_NAND_ECC_STATUS_REPORT
#define XMEDIA_FMC100_ECC_ERR_NUM0_BUF0      0xc0

#define GET_ECC_ERR_NUM(_i, _reg)       (((_reg) >> ((_i) * 8)) & 0xff)
#endif
#define REG_CNT_HIGH_BLOCK_NUM_SHIFT        10

#define REG_CNT_BLOCK_NUM_MASK          0x3ff
#define REG_CNT_BLOCK_NUM_SHIFT         22

#define REG_CNT_PAGE_NUM_MASK           0x3f
#define REG_CNT_PAGE_NUM_SHIFT          16

#define ERR_STR_DRIVER "Driver does not support this configure "
#define ERR_STR_CHECK "Please make sure the hardware configuration is correct"
#define XMEDIA_FMC100_ADDR_CYCLE_MASK        0x2
#define OP_STYPE_NONE           0x0
#define OP_STYPE_READ           0x01
#define OP_STYPE_WRITE          0x02
#define OP_STYPE_ERASE          0x04
#define CLK_FMC_TO_CRG_MHZ(_clk)    ((_clk) * 2000000)
#define MAX_SPI_OP          8
/* SPI general operation parameter */
struct spi_op {
	unsigned char iftype;
	unsigned char cmd;
	unsigned char dummy;
	unsigned int size;
	unsigned int clock;
};

struct spi_drv;

/* SPI interface all operation */
struct xmedia_fmc_spi {
	char *name;
	int chipselect;
	unsigned long long chipsize;
	unsigned int erasesize;
#define SPI_NOR_3BYTE_ADDR_LEN  3   /* address len 3Bytes */
#define SPI_NOR_4BYTE_ADDR_LEN  4   /* address len 4Bytes for 32MB */
	unsigned int addrcycle;

	struct spi_op read[1];
	struct spi_op write[1];
	struct spi_op erase[MAX_SPI_OP];

	void *host;

	struct spi_drv *driver;
};

/* SPI interface special operation function hook */
struct spi_drv {
	int (*wait_ready)(struct xmedia_fmc_spi *spi);
	int (*write_enable)(struct xmedia_fmc_spi *spi);
	int (*qe_enable)(struct xmedia_fmc_spi *spi);
	int (*bus_prepare)(struct xmedia_fmc_spi *spi, int op);
	int (*entry_4addr)(struct xmedia_fmc_spi *spi, int en);
};

struct spi_nand_info {
	char *name;
	unsigned char id[MAX_SPI_NAND_ID_LEN];
	unsigned char id_len;
	unsigned long long chipsize;
	unsigned int erasesize;
	unsigned int pagesize;
	unsigned int oobsize;
#define BBP_LAST_PAGE       0x01
#define BBP_FIRST_PAGE      0x02
	unsigned int badblock_pos;
	struct spi_op *read[MAX_SPI_OP];
	struct spi_op *write[MAX_SPI_OP];
	struct spi_op *erase[MAX_SPI_OP];
	struct spi_drv *driver;
};

extern char spi_nand_feature_op(struct xmedia_fmc_spi *spi, u_char op, u_char addr,
				  u_char *val);

struct xmedia_fmc_host {
	struct mtd_info *mtd;
	struct nand_chip *chip;
	struct xmedia_fmc_spi spi[CONFIG_SPI_NAND_MAX_CHIP_NUM];
	struct fmc_cmd_op cmd_op;

	void __iomem *iobase;
	void __iomem *regbase;
	struct clk *clk;
	u32 clkrate;

	unsigned int fmc_cfg;
	unsigned int fmc_cfg_ecc0;

	unsigned int offset;

	struct device *dev;
	struct mutex *lock;

	/* This is maybe an un-aligment address, only for malloc or free */
	char *buforg;
	char *buffer;

#ifdef CONFIG_64BIT
	unsigned long long dma_buffer;
	unsigned long long dma_oob;
#else
	unsigned int dma_buffer;
	unsigned int dma_oob;
#endif

	unsigned int addr_cycle;
	unsigned int addr_value[2];
	unsigned int cache_addr_value[2];

	unsigned int column;
	unsigned int block_page_mask;

	unsigned int ecctype;
	unsigned int pagesize;
	unsigned int oobsize;

	int add_partition;

	int  need_rr_data;
#define XMEDIA_FMC100_READ_RETRY_DATA_LEN         128
	char rr_data[XMEDIA_FMC100_READ_RETRY_DATA_LEN];
	struct read_retry_t *read_retry;

	int version;

	/* BOOTROM read two bytes to detect the bad block flag */
#define XMEDIA_FMC_BAD_BLOCK_POS     0
	unsigned char *bbm; /* nand bad block mark */
	unsigned short *epm;    /* nand empty page mark */

	unsigned int uc_er;

	void (*send_cmd_write)(struct xmedia_fmc_host *host);
	void (*send_cmd_status)(struct xmedia_fmc_host *host);
	void (*send_cmd_read)(struct xmedia_fmc_host *host);
	void (*send_cmd_erase)(struct xmedia_fmc_host *host);
	void (*send_cmd_readid)(struct xmedia_fmc_host *host);
	void (*send_cmd_reset)(struct xmedia_fmc_host *host);
#ifdef CONFIG_PM
	int (*suspend)(struct platform_device *pltdev, pm_message_t state);
	int (*resume)(struct platform_device *pltdev);
#endif
};

void xmedia_fmc100_ecc0_switch(struct xmedia_fmc_host *host, unsigned char op);

void xmedia_fmc100_spi_nand_init(struct nand_chip *chip);

extern void xmedia_fmc_spi_nand_ids_register(void);

extern void xmedia_fmc_set_nand_system_clock(struct spi_op *op, int clk_en);

#ifdef CONFIG_PM
int xmedia_fmc100_suspend(struct platform_device *pltdev, pm_message_t state);
int xmedia_fmc100_resume(struct platform_device *pltdev);
void xmedia_fmc100_spi_nand_config(struct xmedia_fmc_host *host);
#endif

#endif /* End of __XMEDIA_FMC100_H__ */
